package client.ogl;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.io.IOException;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.util.HashMap;
import java.util.Hashtable;

import javax.swing.ImageIcon;

import org.lwjgl.BufferUtils;

import static org.lwjgl.opengl.GL11.*;

/**
 * 
 * 
 * A utility class to load textures for OpenGL. This source is based
 * on a texture that can be found in the Java Gaming (www.javagaming.org)
 * Wiki. It has been simplified slightly for explicit 2D graphics use.
 *
 * OpenGL uses a particular image format. Since the images that are
 * loaded from disk may not match this format this loader introduces
 * a intermediate image which the source image is copied into. In turn,
 * this image is used as source for the OpenGL texture.
 *
 * @author Kevin Glass
 * @author Brian Matzon 
 * @author Tim Meurer
 * 
 * @version 0.5.0
 * @since 0.5.0
 */
public class TextureLoader {
	
	private static HashMap<String, Texture> texturetable = new HashMap<String, Texture>();
	private static ColorModel glAlphaColorModel;
	private static ColorModel glColorModel;
	private static String directory = "";
	
	private static IntBuffer textureIDBuffer = BufferUtils.createIntBuffer(1);
	
	public static void init()
	{
		glAlphaColorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), new int[] {8,8,8,8}, true, false, ComponentColorModel.TRANSLUCENT, DataBuffer.TYPE_BYTE);
		glColorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), new int[] {8,8,8,8}, false, false, ComponentColorModel.OPAQUE, DataBuffer.TYPE_BYTE);

	}
	
	private static int createTextureID()
	{
		glGenTextures(textureIDBuffer);
		return textureIDBuffer.get(0);
	}
	
	public static synchronized Texture getTexture(String resourceName) throws IOException
	{
		Texture tex = texturetable.get(resourceName);
		
		if(tex != null){
			return tex;
		}
		
		tex = getTexture(resourceName,GL_TEXTURE_2D,GL_RGB,GL_LINEAR,GL_LINEAR);
		
		texturetable.put(resourceName, tex);
		
		return tex;
	}
	
	public static Texture getTexture(String resourceName, int target, int dstPixelFormat, int minFilter, int magFilter) throws IOException
	{
		
		int srcPixelFormat;
		int textureID = createTextureID();
		Texture texture = new Texture(target,textureID);
		
		glBindTexture(target, textureID);
		
		BufferedImage bufimg = loadImage(directory+resourceName);
		texture.setWidth(bufimg.getWidth());
		texture.setHeight(bufimg.getHeight());
		
		if(bufimg.getColorModel().hasAlpha())
		{
			srcPixelFormat = GL_RGB;
		}
		else
		{
			srcPixelFormat = GL_RGB;
		}
		
		ByteBuffer textureBuffer = convertImageData(bufimg,texture);
		
		if(target == GL_TEXTURE_2D)
		{
			glTexParameteri(target, GL_TEXTURE_MIN_FILTER, minFilter);
			glTexParameteri(target, GL_TEXTURE_MAG_FILTER, magFilter);
		}
		
		glTexImage2D(target, 0, dstPixelFormat, get2Fold(bufimg.getWidth()), get2Fold(bufimg.getHeight()), 0, srcPixelFormat, GL_UNSIGNED_BYTE, textureBuffer);
		
		return texture;
	}

	private static int get2Fold(int fold)
	{
		int ret = 2;
		while(ret < fold)
		{
			ret*=2;
		}
		return ret;
	}
	
	private static ByteBuffer convertImageData(BufferedImage bufimg, Texture texture)
	{
		ByteBuffer imageBuffer;
		WritableRaster raster;
		BufferedImage teximg;
		
		int texWidth = 2;
		int texHeight = 2;
		
		while(texWidth < bufimg.getWidth())
		{
			texWidth *= 2;
		}

		while(texHeight < bufimg.getHeight())
		{
			texHeight *= 2;
		}
		
		texture.setTextureHeight(texHeight);
		texture.setTextureWidth(texWidth);		
		
		if(bufimg.getColorModel().hasAlpha())
		{
			raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, 32, 32, 4, null);
			teximg = new BufferedImage(glAlphaColorModel, raster, false, new Hashtable());
		}
		else
		{
			raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, 32, 32, 3, null);
			teximg = new BufferedImage(glColorModel, raster, false, new Hashtable());
		}
		
		Graphics g = teximg.getGraphics();
		g.setColor(new Color(0f,0f,0f,0f));
		g.fillRect(0, 0, texWidth, texHeight);
		
		g.drawImage(bufimg, 0, 0, null);
		
		byte[] data = ((DataBufferByte) teximg.getRaster().getDataBuffer()).getData();
		
		imageBuffer = ByteBuffer.allocateDirect(data.length);
		imageBuffer.order(ByteOrder.nativeOrder());
		imageBuffer.put(data,0,data.length);
		imageBuffer.flip();
		
		return imageBuffer;
		
	}
	
	private static BufferedImage loadImage(String ref) throws IOException
	{
		URL url;
		
		synchronized (TextureLoader.class) {
			url = TextureLoader.class.getClassLoader().getResource(ref);
		}
		
		if(url == null)
		{
			throw new IOException("Cannot find: "+ref);
		}
		
		Image img = new ImageIcon(url).getImage();
		BufferedImage bufferedImage = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_RGB);
		Graphics g = bufferedImage.getGraphics();
		g.drawImage(img, 0, 0, null);
		g.dispose();
		
		return bufferedImage;
	}
	
	public static void setDirectory(String directory)
	{
		synchronized (directory) {
			TextureLoader.directory = directory;
		}
	}
	
	public static String getDirectory()
	{
		synchronized (directory) {
			return directory;
		}
	}
	
}


